#include "policy.h"
#include <stdio.h>
#include <deque>

#pragma warning(disable:4996)

struct TimeSeries {
	TimeSeries(size_t s) : size(s)
	{
	}
	const size_t size = 0;

	std::deque<double> buffer;
	double accu = 0;

	void Clear()
	{
		buffer.clear();
		accu = 0;
	}

	void Push(double data)
	{
		if (buffer.size() >= size)
		{
			accu -= buffer.front();
			buffer.pop_front();
		}
		buffer.push_back(data);
		accu += data;
	}

	bool IsReady()
	{
		return buffer.size() == size;
	}

	double MoveAverge()
	{
		return accu / buffer.size();
	}

	double MoveAverge(size_t start, size_t count)
	{
		double sum = 0;
		for (size_t i = start; i < start + count; ++i)
		{
			sum += buffer[i];
		}
		return sum / count;
	}

	double MoveAvergeFromLast(size_t start, size_t count)
	{
		double sum = 0;
		for (size_t i = buffer.size() - start - count; i < buffer.size() - start; ++i)
		{
			sum += buffer[i];
		}
		return sum / count;
	}

	double ChangeRate(size_t start, size_t end)
	{
		return buffer[start] / buffer[end] - 1;
	}
};

double open_price = 0;
int no_send_count = 0;
double open_volume = 0;
double last_accu_volume = 0;
double best_price = 0;

std::deque<double> volume_buffer;
double mv_day_volume = 0;

TimeSeries ts_volume(120);
TimeSeries ts_price(120);


void Clear() 
{
	open_price = 0;
	open_volume = 0;
	no_send_count = 0;
	last_accu_volume = 0;
	best_price = 0;
}

FILE* file = nullptr;
const static bool log_flag = false;

int policy_position = 0;


#define PRINT_INFO() printf("%s %lf %lf %lf %lf %lf %lf %lf\n", \
	md->update_time, change_rate_30, change_rate_30_2, change_rate_60, volume_total, volume_total_2, ma1, ma2);

#define LONG() {if (md->ask_price > 1e10 || md->ask_price < 0.01 || md->ask_price > md->upper_limit * 0.98) return; \
	target_position = 1; \
	policy_position = 1; \
	open_price = md->last_price; \
	best_price = md->last_price; \
	return;}

#define SHORT() {return;if (md->bid_price > 1e10 || md->bid_price < 0.01 || md->bid_price < md->lower_limit * 1.02) return; \
	target_position = -1; \
	policy_position = -1; \
	open_price = md->last_price; \
	best_price = md->last_price; \
	return;}

#define CLOSE(str) {if ((target_position > 0 && md->bid_price > 1e10 )||(target_position < 0 && md->ask_price > 1e10 )) return; \
	target_position = 0; \
	policy_position = 0; \
	best_price = 0; \
	remark = str; \
	return;}

void Policy::OnMarketData(const MarketData * md)
{
	ts_price.Push(md->last_price);
	ts_volume.Push(md->volume);

	if (!ts_price.IsReady() || !ts_volume.IsReady())
	{
		return;
	}

	int rolling_count = 5;
	double last_mid_price = ts_price.MoveAvergeFromLast(0, rolling_count);
	double last_mid_price_30 = ts_price.MoveAvergeFromLast(rolling_count * 1, rolling_count);
	double last_mid_price_60 = ts_price.MoveAvergeFromLast(rolling_count * 2, rolling_count);
	double last_mid_price_90 = ts_price.MoveAvergeFromLast(rolling_count * 3, rolling_count);
	double last_mid_price_120 = ts_price.MoveAvergeFromLast(rolling_count * 4, rolling_count);

	double change_rate_30 = last_mid_price / last_mid_price_30 - 1;
	double change_rate_30_2 = last_mid_price_30 / last_mid_price_60 - 1;
	double change_rate_30_3 = last_mid_price_60 / last_mid_price_90 - 1;
	double change_rate_30_4 = last_mid_price_90 / last_mid_price_120 - 1;

	double change_rate_60 = last_mid_price / last_mid_price_60 / 2 - 0.5;


	int volume_rolling_count = 30;
	double volume_total = ts_volume.MoveAvergeFromLast(0, volume_rolling_count) * volume_rolling_count;
	double volume_total_2 = ts_volume.MoveAvergeFromLast(volume_rolling_count, volume_rolling_count) * volume_rolling_count;

	double volume_change_rate_1 = volume_total / volume_total_2;

	double threshold_price_30 = 0.0018 / (1 + sqrt(volume_change_rate_1) / 5);
	double threshold_price_30_2 = 0.0012 / (1 + sqrt(volume_change_rate_1) / 5);
	double threshold_price_30_3 = 0.0008 / (1 + sqrt(volume_change_rate_1) / 5);

	const static double threshold_volume = mv_day_volume * (1 - abs(target_position));


	if (log_flag) {
		fprintf(file, "%s,%s,change,%lf,%lf,%lf,%lf,%lf,(%lf),volume,%lf,%lf,price,%lf,mv,%lf,%lf,%lf\n",
			md->trading_day, md->update_time,
			change_rate_30, change_rate_30_2, change_rate_30_3, change_rate_30_4, change_rate_60,
			threshold_price_30,
			volume_total, volume_total_2,
			md->last_price,
			last_mid_price, last_mid_price_30, last_mid_price_60
		);
	}

	//if (no_send_count > 0)
	//{
	//	no_send_count--;
	//	return;
	//}

	if ((volume_change_rate_1 > 2 && volume_total > threshold_volume))
		/*||
		(volume_change_rate_1 > 1 && volume_total_2 > 2 * threshold_volume) ||
		(volume_change_rate_1 > 2 && volume_total_2 > threshold_volume) ||
		(volume_change_rate_1 > 3 && volume_total > 2 * threshold_volume))*/
	{

		if (change_rate_30 > change_rate_30_2 && 
			change_rate_30_2 > change_rate_30_3 && 
			change_rate_30_3 > 0 && 
			change_rate_30 > threshold_price_30 && 
			change_rate_30_2 > threshold_price_30_2 && 
			change_rate_30_3 > threshold_price_30_3)
		{
			if (target_position == 0)
			{
				LONG();
			}
			if (target_position < 0)
			{
				CLOSE("reverse");
			}
		}

		if (change_rate_30 < change_rate_30_2 && 
			change_rate_30_2 < change_rate_30_3 && 
			change_rate_30_3 < 0 && 
			change_rate_30 < -threshold_price_30 && 
			change_rate_30_2 < -threshold_price_30_2 && 
			change_rate_30_3 < -threshold_price_30_3)
		{
			if (target_position == 0)
			{
				SHORT();
			}
			if (target_position > 0)
			{
				CLOSE("reverse");
			}
		}
	}

	if (target_position > 0)
	{
		if (best_price == 0)
		{
			best_price = open_price;
		}
		if (last_mid_price > best_price)
		{
			best_price = last_mid_price;
		}
		if (md->high_price > best_price)
		{
			best_price = md->high_price;
		}
		//if (best_price > open_price * (1 + 0.005) && last_mid_price > open_price && last_mid_price < best_price * (1 - 0.001))
		//{
		//	printf("Policy: stop profit1\n");
		//	CLOSE();
		//}
		//if (volume_total > open_volume * 0.7 && last_mid_price < open_price * (1 - 0.001))
		//{
		//	printf("Policy: stop loss1\n");
		//	PRINT_INFO();
		//	target_position = 0;
		//	no_send_count = 120;
		//	return;
		//}
		if (md->last_price < open_price - 30)
		{
			CLOSE("loss");
		}
		if (change_rate_30 < change_rate_30_2 && change_rate_30_2 < change_rate_30_3 && change_rate_30_3 < 0 && change_rate_30 < -threshold_price_30 * 0.5)
		{
			CLOSE("finish");
		}
	}
	if (target_position < 0)
	{
		if (best_price == 0)
		{
			best_price = open_price;
		}
		if (last_mid_price < best_price)
		{
			best_price = last_mid_price;
		}
		//if (best_price < open_price * (1 - 0.005)  && last_mid_price < open_price && last_mid_price > best_price * (1 + 0.001))
		//{
		//	printf("Policy: stop profit1\n");
		//	CLOSE();
		//}
		//if (volume_total > open_volume * 0.7 && last_mid_price > open_price * (1 + 0.001))
		//{
		//	printf("Policy: stop loss3\n");
		//	PRINT_INFO();
		//	target_position = 0;
		//	no_send_count = 120;
		//	return;
		//}
		if (md->last_price > open_price + 30)
		{
			CLOSE("loss");
		}
		if (change_rate_30 > change_rate_30_2 && change_rate_30_2 > change_rate_30_3 && change_rate_30_3 > 0 && change_rate_30 > threshold_price_30 * 0.5)
		{
			CLOSE("finish");
		}
	}
}

void Policy::SwitchDay(const std::string trading_day)
{
	if (volume_buffer.size() > 5) 
	{
		volume_buffer.pop_back();
	}
	if (last_accu_volume > 0)
	{
		volume_buffer.push_front(last_accu_volume);
	}

	double sum = 0;
	for (size_t i = 0; i < volume_buffer.size(); ++i)
	{
		sum += volume_buffer[i];
	}
	if (volume_buffer.size() > 0)
	{
		mv_day_volume = sum / volume_buffer.size() / 570;
	}
	
	if (mv_day_volume < 3000)
	{
		mv_day_volume = 3000;
	}

	mv_day_volume = 0;

	printf("==Switch day %s, volume %lf\n", trading_day.c_str(), mv_day_volume);

	//Clear();
	if (log_flag)
	{
		if (file != nullptr) 
		{
			fclose(file);
		}
		file = fopen(("./log/" + trading_day + ".csv").c_str(), "w");
	}
}
